local function save_balances() end
local function get_database() end
local function validate_currency() end
local function get_valid_value() end

local storage = minetest.get_mod_storage()
local registered_currencies = {}


--[[
    def = {
        min_value : number =
            the smallest balance that a player can have.

        max_value : number =
            the biggest balance that a player can have.
        
        negative_balance : bool =
            if the balance can be less than 0.
    }
]]
function currencies.register(currency, def)
    def = def or {}
    registered_currencies[currency] = def
end



function currencies.set(currency, pl_name, value)
    validate_currency(currency)

    local balances = get_database().balances
    balances[pl_name] = balances[pl_name] or {}
    balances[pl_name][currency] = get_valid_value(currency, value)

    save_balances(balances)
end



function currencies.add(currency, pl_name, amount)
    currencies.set(currency, pl_name, currencies.get(currency, pl_name) + amount) 
end



function currencies.sub(currency, pl_name, amount)
    currencies.set(currency, pl_name, currencies.get(currency, pl_name) - amount) 
end



function currencies.transfer(currency, from_pl_name, to_pl_name, amount)
    currencies.sub(currency, from_pl_name, amount)
    currencies.add(currency, to_pl_name, amount)
end



function currencies.exchange(currency, from_pl_name, from_amount, to_pl_name, to_amount)
    currencies.sub(currency, from_pl_name, from_amount)
    currencies.add(currency, to_pl_name, to_amount)
end



function currencies.get(currency, pl_name)
    validate_currency(currency)

    local balances = get_database().balances
    balances[pl_name] = balances[pl_name] or {}
    balances[pl_name][currency] = balances[pl_name][currency] or 0
    
    return balances[pl_name][currency]
end



function currencies.exists(currency)
    return registered_currencies[currency]
end



-- If currency is nil it'll reset every currency.
function currencies.clear_balance(currency, pl_name)
    if currency then 
        validate_currency(currency)
        currencies.set(currency, pl_name, 0)
        return
    end

    local balances = get_database().balances
    balances[pl_name] = {}

    save_balances(balances)
end



function currencies.remove_unregistered()
    local balances = get_database().balances

    for pl_name, pl_currencies in pairs(balances) do
        for currency, balance in pairs(pl_currencies) do
            if not currencies.exists(currency) then 
                pl_currencies[currency] = nil
            end 
        end 
    end

    save_balances(balances)
end



function save_balances(balances)
    local db = get_database()
    db.balances = balances
    
    storage:set_string("database", minetest.serialize(db))
end



--[[
    database = {
        balances = {
            pl_name : string = {
                currency_1 : string = value : number, 
                ...
            },
            ...
        }
    }
]]
function get_database()
    local db = minetest.deserialize(storage:get_string("database"))
    -- Recreating the db selecting specific tables, to avoid copying a dirty datababe.
    local final_db = {}
    final_db.balances = db.balances or {}

    return final_db
end



function validate_currency(currency)
    assert(
        registered_currencies[currency], 
        "A mod is trying to access to the " .. currency .. " currency, but it hasn't been registered."
    )
end



function get_valid_value(currency, value)
    local currency_def = registered_currencies[currency]

    if currency_def.negative_balance == false and value < 0 then value = 0 end

    if currency_def.min_value and value < currency_def.min_value then 
        value = currency_def.min_value 
    end

    if currency_def.max_value and value > currency_def.max_value then 
        value = currency_def.max_value 
    end

    return value
end